/**
 * author:0283000106
 */
package com.zte.claa.inficombo.csa.app.service.gprs.mina;

import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.BytesArrayForCC;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMessage;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMsgBody;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMsgHead;
import com.zte.claa.inficombo.csa.app.service.rptparse.CRC16M;
import com.zte.claa.inficombo.csa.app.util.EdianUtil;


public class GPRSDevMsgDecoder implements MessageDecoder {
	// logger
	private static final Logger LOG = LoggerFactory.getLogger(GPRSDevMsgDecoder.class);
	// 字符集
	private Charset charset = null;

	public GPRSDevMsgDecoder(Charset charset) {
		this.charset = charset;
		if (this.charset == null) {
			this.charset = Charset.forName("UTF-8");
		}
	}
	/*
	 * decodable()方法有三个返回值，分别表示如下的含义：
		A. MessageDecoderResult.NOT_OK：表示这个解码器不适合解码数据，然后检查其它解码
		器，如果都不满足会抛异常；
		B. MessageDecoderResult.NEED_DATA：表示当前的读入的数据不够判断是否能够使用这
		个解码器解码，然后再次调用decodable()方法检查其它解码器，如果都是NEED_DATA,
		则等待下次输入；
		C. MessageDecoderResult.OK： 表示这个解码器可以解码读入的数据， 然后则调用
		MessageDecoder 的decode()方法。
		这里注意decodable()方法对参数IoBuffer in 的任何操作在方法结束之后，都会复原，也就是
		你不必担心在调用decode()方法时，position 已经不在缓冲区的起始位置。这个方法相当于
		是预读取，用于判断是否是可用的解码器。
	 * */
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		LOG.info("Received gprs data from remoteAddr=["+session.getRemoteAddress().toString()+"],data= ["+in.getHexDump()+"]");
		if (in.remaining() < GPRSMsgHead.getHeadSize()) {
			LOG.info("[1].Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
			return MessageDecoderResult.NEED_DATA;
		}
		try {	
			byte[] tmpBs = new byte[9];
			in.get(tmpBs);
			int msgtype = tmpBs[0];
			if(GPRSMsgHead.GPRS_HBREQ == msgtype){
				if(in.remaining() < 2){
					LOG.info("[2].Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
					return MessageDecoderResult.NEED_DATA;
				}						
			}else if(GPRSMsgHead.GPRS_REGREQ == msgtype ){
				if(in.remaining() < 23){
					LOG.info("[3].Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
					return MessageDecoderResult.NEED_DATA;
				}						
			}else if(GPRSMsgHead.GPRS_UPLINK == msgtype){
				if(in.remaining() < 4){
					LOG.info("[4].Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
					return MessageDecoderResult.NEED_DATA;
				}else{
					tmpBs = new byte[2];
					in.get(tmpBs);
					int len = Short.toUnsignedInt(EdianUtil.byteToShort_HL(tmpBs, 0));
					if (in.remaining() < len+2) {
						LOG.info("[5].Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
						return MessageDecoderResult.NEED_DATA;
					}
				}
			}else{
				LOG.error("[6].Current data is not correct,msgtype:"+msgtype+",HexData:["+in.getHexDump()+"]");
				return MessageDecoderResult.NOT_OK;
			}
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decodable error: "+e.getMessage(), e);
		}
		return MessageDecoderResult.OK;
	}
	/*
	 * decode()方法有三个返回值，分别表示如下的含义：
		A. MessageDecoderResult.NOT_OK：表示解码失败，会抛异常；
		B. MessageDecoderResult.NEED_DATA：表示数据不够，需要读到新的数据后，再次调用
		decode()方法。
		C. MessageDecoderResult.OK：表示解码成功。
	 * */
	public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		LOG.info("begin to decode gprs data[sessionid=" + session.getId() + "]...");
		try {
			GPRSMsgHead msgHead = new GPRSMsgHead();
			GPRSMsgBody msgBody = new GPRSMsgBody();
			GPRSMessage msg     = new GPRSMessage();
			BytesArrayForCC bytesCC = new BytesArrayForCC(in.capacity());
			// 解码消息类型
			Byte databyte = in.get();
			bytesCC.push((byte)databyte); 
			int msgtype = Byte.toUnsignedInt(databyte);
			msgHead.setMsgtype(msgtype);
			// 解码设备deveui
			byte[] tmpBs = new byte[8];
			in.get(tmpBs);
			bytesCC.push(tmpBs);
			String deveui = bytesToHexStr(tmpBs);
			msgHead.setDeveui(deveui);			
			if(GPRSMsgHead.GPRS_REGREQ == msgtype){				
				// 解码iccid
				tmpBs = new byte[20];
				in.get(tmpBs);
				bytesCC.push(tmpBs);
				String iccid = new String(tmpBs,"UTF-8");
				msgHead.setIccid(iccid);
				// 解码rssi
				databyte = in.get();
				bytesCC.push((byte)databyte); 
				int rssi = Byte.toUnsignedInt(databyte);
				msgHead.setRssi(rssi);
				// 设置消息总长度
				msgHead.setMsgLen(32);				
			}else if(GPRSMsgHead.GPRS_HBREQ == msgtype){
				// 设置消息总长度
				msgHead.setMsgLen(11);
			}else if(GPRSMsgHead.GPRS_UPLINK == msgtype){
				int remainLen = in.remaining();
				if(remainLen >= 4){
					tmpBs = new byte[2];
					in.get(tmpBs);
					bytesCC.push(tmpBs);
					int loraLoadLen = bytes2UintByBigEnd(tmpBs);					
					msgHead.setMsgLen(loraLoadLen+13);					
					tmpBs = new byte[loraLoadLen];
					in.get(tmpBs, 0, loraLoadLen);
					bytesCC.push(tmpBs);
					msgBody.setData(tmpBs);
					msgBody.setMsgLen(loraLoadLen);
				}
			}
			// 解码crc16校验码
			tmpBs = new byte[2];
			in.get(tmpBs);
			bytesCC.push(tmpBs);			
			boolean crcflag = CRC16M.checkBufBigEnd(bytesCC.getBytes());
			if(true != crcflag){
				msg.setErrtype(GPRSMessage.ERR_CRCCHECK);
				LOG.error("***[session=" + session.getId()+"]decode error,crc check is failed");
				this.processInvalidMsg(session, in);
				return MessageDecoderResult.OK;
			}		
			msg.setBody(msgBody);
			msg.setHead(msgHead);
			
			// 返回解码结果
			out.write(msg);
			LOG.info("OK: finish decoding[session=" + session.getId()+", msg: "+msg.toString());
			return MessageDecoderResult.OK;			
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decode error:"+e.getMessage(), e);
			this.processInvalidMsg(session, in);
		}
		return MessageDecoderResult.OK;
	}
	

	private void processInvalidMsg(IoSession session, IoBuffer in){
		// in.getHexDump() 不会调整pos和limit等指针.
		LOG.warn("Buffer decode: gprs data is invalid, pos="+in.position()+", limit="+in.limit()+", data="+in.getHexDump());
		int pos = in.position();
		int limit = in.limit();
		int leftSize = in.remaining();
		in.skip(leftSize);
		LOG.warn("***[session=" + session.getId()+"]skip invalid data:　pos="+pos+", limit="+limit+", leftSize="+leftSize+", left-data="+in.getHexDump());
	}


	public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
		LOG.debug("FinishDecode for session: " + session.getId());
	}
	
    public static final String bytesToHexStr(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
         sTemp = Integer.toHexString(0xFF & bArray[i]);
         if (sTemp.length() < 2)
          sb.append(0);
         sb.append(sTemp.toLowerCase());
        }
        return sb.toString();
    }
	
    public static byte[] byteMerger(byte[] bt1, byte[] bt2){  
        byte[] bt3 = new byte[bt1.length+bt2.length];  
        System.arraycopy(bt1, 0, bt3, 0, bt1.length);  
        System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);  
        return bt3;  
    } 
    
    public static int bytes2UintByBigEnd(byte[] bytes){
    	int retval = 0;
    	if(null == bytes || bytes.length == 0){
    		return 0;
    	}    	
    	for(int i=0; i<bytes.length; i++){
    		retval = retval*256 + Byte.toUnsignedInt(bytes[i]);
    	}
    	return retval;
    }
    
    public static int bytes2UintByLittleEnd(byte[] bytes){
    	int retval = 0;
    	if(null == bytes || bytes.length == 0){
    		return 0;
    	}
    	for(int i=0; i<bytes.length; i++){
    		retval = retval*256 + Byte.toUnsignedInt(bytes[bytes.length-i-1]);
    	}
    	return retval;
    }
}
